package com.yfq.springframework.aop.framework.autoproxy;

import com.yfq.springframework.aop.*;
import com.yfq.springframework.aop.aspectj.AspectjExpressionPointcutAdvisor;
import com.yfq.springframework.aop.framework.ProxyFactory;
import com.yfq.springframework.beans.BeanFactoryAware;
import com.yfq.springframework.beans.BeansException;
import com.yfq.springframework.beans.config.InstantiationAwareBeanPostProcessor;
import com.yfq.springframework.beans.factory.BeanFactory;
import com.yfq.springframework.beans.support.DefaultListableBeanFactory;
import org.aopalliance.aop.Advice;
import org.aopalliance.intercept.MethodInterceptor;

import java.util.Collection;

/**
 * @author: yfq
 * @date: 2023/2/3 10:11
 * @description: 自动代理类，将Aop工具融合到SpringBean的生命周期中
 */
public class DefaultAdvisorAutoProxyCreator implements InstantiationAwareBeanPostProcessor, BeanFactoryAware {

    private DefaultListableBeanFactory beanFactory;

    @Override
    public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
        if (isInfrastructureClass(beanClass)) {
            return null;
        }

        //获取所有的PointcutAdvisor
        Collection<AspectjExpressionPointcutAdvisor> pointcutAdvisors = beanFactory.getBeansOfType(AspectjExpressionPointcutAdvisor.class).values();
        for (AspectjExpressionPointcutAdvisor advisor : pointcutAdvisors) {
            ClassFilter classFilter = advisor.getPointcut().getClassFilter();
            if (!classFilter.matches(beanClass)) {
                continue;
            }

            AdvisedSupport advisedSupport = new AdvisedSupport();
            try {
                TargetSource targetSource = new TargetSource(beanClass.getDeclaredConstructor().newInstance());
                advisedSupport.setTargetSource(targetSource);
                advisedSupport.setProxyTargetClass(false);
                advisedSupport.setMethodInterceptor((MethodInterceptor) advisor.getAdvice());
                advisedSupport.setMethodMatcher(advisor.getPointcut().getMethodMatcher());
            } catch (Exception e) {
                e.printStackTrace();
            }

            return new ProxyFactory(advisedSupport).getProxy();
        }

        return null;
    }

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        this.beanFactory = (DefaultListableBeanFactory) beanFactory;
    }

    private boolean isInfrastructureClass(Class<?> beanClass) {
        return Advice.class.isAssignableFrom(beanClass) ||
                Pointcut.class.isAssignableFrom(beanClass) ||
                Advisor.class.isAssignableFrom(beanClass);
    }
}
